package org.fujay.langchain4jserver.infrastructure.persistence.assembler;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.fujay.commons.core.util.JsonUtils;
import org.fujay.langchain4jserver.domain.application.aggregate.AppAggregate;
import org.fujay.langchain4jserver.domain.application.entity.App;
import org.fujay.langchain4jserver.domain.application.entity.AppConfigHistory;
import org.fujay.langchain4jserver.domain.application.entity.AppUsageStats;
import org.fujay.langchain4jserver.domain.application.vo.AppConfigSnapshot;
import org.fujay.langchain4jserver.domain.application.vo.AppId;
import org.fujay.langchain4jserver.domain.application.vo.Version;
import org.fujay.langchain4jserver.domain.knowledge.vo.KnowledgeBaseId;
import org.fujay.langchain4jserver.domain.model.vo.ModelId;
import org.fujay.langchain4jserver.infrastructure.persistence.mybatis.entity.AppConfigHistoryPo;
import org.fujay.langchain4jserver.infrastructure.persistence.mybatis.entity.AppPo;
import org.fujay.langchain4jserver.infrastructure.persistence.mybatis.entity.AppUsageStatsPo;

import java.util.Collections;

@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class AppAssembler {


    public static App toDomainEntity(final AppPo po) {
        if (po == null) {
            return null;
        }
        return App.load(
                new AppId(po.getId()),
                po.getAppName(),
                po.getAppDesc(),
                po.getModelId() != null ? new ModelId(po.getModelId()) : null,
                po.getUseKnowledgeBase(),
                po.getKnowledgeBaseId() != null ? new KnowledgeBaseId(po.getKnowledgeBaseId()) : null,
                po.getCreateTime(),
                po.getUpdateTime());
    }

    public static AppPo toPO(final AppAggregate aggregate) {
        if (aggregate == null) {
            return null;
        }
        App app = aggregate.getApp();

        AppPo po = new AppPo();
        po.setId(app.getId().value());
        po.setAppName(app.getAppName());
        po.setAppDesc(app.getAppDesc());
        po.setModelId(app.getModelId().value());
        po.setUseKnowledgeBase(app.isUseKnowledgeBase());
        po.setKnowledgeBaseId(app.getKnowledgeBaseId() != null ? app.getKnowledgeBaseId().value() : null);

        return po;
    }

    /**
     * @param po AppPO
     * @return Shallow AppAggregate
     */
    public static AppAggregate toAggregateShallow(final AppPo po) {
        if (po == null) {
            return null;
        }
        App app = toDomainEntity(po);
        return AppAggregate.load(app, Collections.emptyList()); // Corrected signature
    }

    // --- AppConfigHistory <-> AppConfigHistoryPO --- //

    /**
     * Converts AppConfigHistoryPO persistent object to AppConfigHistory domain object.
     *
     * @param po The AppConfigHistoryPO persistent object.
     * @return The AppConfigHistory domain object.
     */
    public static AppConfigHistory toHistoryEntity(final AppConfigHistoryPo po) {
        if (po == null) {
            return null;
        }
        AppConfigSnapshot snapshot = deserializeSnapshot(po.getConfigSnapshotJson());
        // Assuming Version needs to be reconstructed or is part of PO
        // If version is just a number stored in PO, create it:
        // Version version = new Version(po.getVersion()); // Requires PO to have
        // version field
        // For now, let's assume Version needs more complex loading or isn't directly
        // available
        // Placeholder: Use Version.initial() if Version is missing, needs review
        Version version = Version.initial(); // TODO: Check how Version should be loaded from PO

        return AppConfigHistory.load(
                po.getId(),
                new AppId(po.getAppId()),
                version, // Pass the loaded/created Version
                snapshot,
                po.getChangeDescription(), // Assuming PO has this field
                po.getCreateBy(), // Using createBy from BaseEntity as changedBy
                po.getCreateTime() // Using createTime from BaseEntity as changeTime
        );
    }

    /**
     * Converts AppConfigHistory domain object to AppConfigHistoryPO persistent object.
     *
     * @param history The AppConfigHistory domain object.
     * @return The AppConfigHistoryPO persistent object.
     */
    public static AppConfigHistoryPo toHistoryPO(final AppConfigHistory history) {
        if (history == null) {
            return null;
        }
        AppConfigHistoryPo po = new AppConfigHistoryPo();
        po.setId(history.getId());
        po.setAppId(history.getAppId() != null ? history.getAppId().value() : null);
        po.setVersion(
                history.getVersion() != null ? history.getVersion().value() : null); // Assuming Version has
        // value() and PO has
        // setVersion()
        po.setConfigSnapshotJson(serializeSnapshot(history.getConfigSnapshot()));
        po.setChangeDescription(history.getChangeDescription()); // Assuming PO has this field
        // BaseEntity fields are usually handled by fill strategy, but map
        // changedBy/Time here
        po.setCreateBy(history.getChangedBy()); // Map changedBy to createBy
        po.setCreateTime(history.getChangeTime()); // Map changeTime to createTime
        // updateBy/updateTime might not be relevant for immutable history
        return po;
    }

    // --- AppUsageStats <-> AppUsageStatsPO --- //

    /**
     * Converts AppUsageStatsPO to AppUsageStats domain entity.
     *
     * @param po AppUsageStatsPO
     * @return AppUsageStats
     */
    public static AppUsageStats toStatsEntity(final AppUsageStatsPo po) {
        if (po == null) {
            return null;
        }
        return new AppUsageStats(
                new AppId(po.getAppId()),
                po.getDate(),
                po.getRequestCount(),
                po.getPromptTokens(),
                po.getCompletionTokens(),
                po.getTotalTokens());
        // Note: PO's ID and BaseEntity fields are not mapped to the domain entity
    }

    /**
     * Converts AppUsageStats domain entity to AppUsageStatsPO.
     *
     * @param stats AppUsageStats
     * @return AppUsageStatsPO
     */
    public static AppUsageStatsPo toStatsPO(final AppUsageStats stats) {
        if (stats == null) {
            return null;
        }
        AppUsageStatsPo po = new AppUsageStatsPo();
        po.setAppId(stats.getAppId() != null ? stats.getAppId().value() : null);
        po.setDate(stats.getDate());
        po.setRequestCount(stats.getRequestCount());
        po.setPromptTokens(stats.getPromptTokens());
        po.setCompletionTokens(stats.getCompletionTokens());
        po.setTotalTokens(stats.getTotalTokens());
        // ID is usually auto-generated or set during repository save logic if updating.
        // BaseEntity fields handled by MyBatis-Plus.
        return po;
    }


    public static String serializeSnapshot(final AppConfigSnapshot snapshot) {
        if (snapshot == null) {
            return null;
        }
        return JsonUtils.toJsonString(snapshot);
    }

    public  static AppConfigSnapshot deserializeSnapshot(final String json) {
        if (json == null || json.isEmpty()) {
            return null;
        }
        return JsonUtils.parseObject(json, AppConfigSnapshot.class);
    }
}
